﻿#pragma execution_character_set("utf-8")

#include "SpiderDataDlg.h"

#include <QAction>
#include <QBoxLayout>
#include <QCheckBox>
#include <QJsonArray>
#include <QJsonObject>
#include <QLabel>
#include <QLineEdit>
#include <QPushButton>
#include <QString>
#include <QStringList>

#include "HttpPost.h"
#include "QFile"
#include "QTimer"

SpiderDataDlg::SpiderDataDlg(QWidget* parent)
    : QDialog(parent)
{
    setWindowFlags(Qt::WindowCloseButtonHint | Qt::WindowMaximizeButtonHint);

    QVBoxLayout* layout = new QVBoxLayout();
    layout->setMargin(0);
    m_log = new QTextEdit();
    m_log->setReadOnly(true);

    m_log->setStyleSheet(R"(
    QTextEdit {
        font-size: 18pt;
        font-weight: bold;
        background-color: black;
        color: white;
    }
)");

    auto btn1 = new QPushButton("AtCoder");
    connect(btn1, &QPushButton::clicked, this, &SpiderDataDlg::slotAtCoder);
    QHBoxLayout* layoutBtn = new QHBoxLayout();
    layoutBtn->addWidget(btn1);

    auto btn2 = new QPushButton("Codeforces");
    connect(btn2, &QPushButton::clicked, this, &SpiderDataDlg::slotCodeforces);
    layoutBtn->addWidget(btn2);
    layoutBtn->addStretch();

    auto btn3 = new QPushButton("GetCFTagFromLuogu");
    connect(btn3, &QPushButton::clicked, this, &SpiderDataDlg::slotLuoguCF);
    layoutBtn->addWidget(btn3);

    auto btn4 = new QPushButton("GetATTagFromLuogu");
    connect(btn4, &QPushButton::clicked, this, &SpiderDataDlg::slotLuoguAT);
    layoutBtn->addWidget(btn4);

    auto btn5 = new QPushButton("Stop");
    connect(btn5, &QPushButton::clicked, this, [this] { m_bStop = true; });
    layoutBtn->addWidget(btn5);

    layout->addLayout(layoutBtn);
    layout->addWidget(m_log);

    setLayout(layout);

    showMaximized();
}

void SpiderDataDlg::addInfo(const QString& str)
{
    m_log->append(str);
    m_log->moveCursor(QTextCursor::End);
}

void SpiderDataDlg::onePostAtPage(const QString& strID,
                                  const QString& strLocalFile)
{
    QJsonArray jarAll;

    int nStartSecond = 0;
    while (true)
    {
        const QString strUrl =
            QString(
                "https://kenkoooo.com/atcoder/atcoder-api/v3/"
                "user/submissions?user=%1&from_second=%2")
                .arg(strID)
                .arg(nStartSecond);
        addInfo(QString("请求:%1 \n结果保存到本地:%2")
                    .arg(strUrl)
                    .arg(strLocalFile));

        const bool bResult = post.post(strUrl, strLocalFile);
        if (!bResult)
        {
            addInfo("Error:" + post.getLastError());
            break;
        }
        else
        {
            QJsonArray jarTmp = DataMgr::readJsonArray(strLocalFile);
            if (jarTmp.size() == 0)
            {
                break;
            }

            nStartSecond =
                jarTmp.last().toObject().value("epoch_second").toInt() + 1;

            for (const auto& v : jarTmp)
            {
                jarAll.push_back(v);
            }

            addInfo("OK.");
        }
    }

    QFile file(strLocalFile);
    if (file.open(QIODevice::Truncate | QIODevice::WriteOnly))
    {
        QJsonDocument jsonDoc(jarAll);
        file.write(jsonDoc.toJson());
        file.close();
    }

    addInfo("\r\n");
}

bool SpiderDataDlg::onePost(const QString& strUrl, const QString& strLocalFile)
{
    bool bRet = false;
    QString strFormat = "请求:%1 \n结果保存到本地:%2";

    addInfo(strFormat.arg(strUrl).arg(strLocalFile));
    // Contest
    const bool bResult = post.post(strUrl, strLocalFile);
    if (!bResult)
    {
        addInfo("Error:" + post.getLastError());
    }
    else
    {
        addInfo("OK.");
        bRet = true;
    }

    addInfo("\r\n");

    return bRet;
}

void SpiderDataDlg::slotAtCoder()
{
    auto m_dataMgr = DataMgr::getInstance();

    if (!m_bStartup) m_log->clear();
    post.setAtcoder(true);
    const QString strTempPath = m_dataMgr->m_strDataDir + "/Atcoder/";

    onePost("https://kenkoooo.com/atcoder/resources/contests.json",
            strTempPath + "contestList.json");
    onePost("https://kenkoooo.com/atcoder/resources/problems.json",
            strTempPath + "problemList.json");
    onePost("https://kenkoooo.com/atcoder/resources/problem-models.json",
            strTempPath + "problem-models.json");

    addInfo("请求相关用户AC题目列表。");
    std::vector<QString> vecUser = m_dataMgr->m_vecATOtherUsers;
    vecUser.push_back("weixirui");
    for (const auto& id : vecUser)
    {
        addInfo(id);
        onePostAtPage(id, strTempPath + id + ".json");
    }
}

void SpiderDataDlg::slotCodeforces()
{
    post.setAtcoder(false);

    if (!m_bStartup) m_log->clear();

    auto m_dataMgr = DataMgr::getInstance();

    const QString strTempPath = m_dataMgr->m_strDataDir + "/Codeforces/";

    onePost("https://codeforces.com/api/contest.list?gym=false",
            strTempPath + "contestList.json");
    onePost("https://codeforces.com/api/problemset.problems?tags=",
            strTempPath + "problemList.json");

    addInfo("请求相关用户AC题目列表。");
    std::vector<QString> vecUser = m_dataMgr->m_vecCFOtherUsers;
    vecUser.push_back("weixirui");
    for (const auto& id : vecUser)
    {
        addInfo(id);
        onePost("https://codeforces.com/api/user.status?handle=" + id,
                strTempPath + id + ".json");
    }
}

void qt_sleep(int milliseconds)
{
    QEventLoop loop;
    QTimer::singleShot(milliseconds, &loop, [&loop]() { loop.quit(); });
    loop.exec();
}

void SpiderDataDlg::slotLuoguAT()
{
    m_bStop = false;
    m_log->clear();

    DataMgr* dataMgr = DataMgr::getInstance();
    const std::vector<ATProblemKey> setID = dataMgr->getATProblemKey();
    for (const auto& id : setID)
    {
        if (m_bStop) break;
        ATContestRef contest = dataMgr->m_mapATContest[id.strContestID];
        if (contest == nullptr)
        {
            addInfo("get contest is failed.");
            continue;
        }

        auto it = dataMgr->m_mapATLuoTag.find(id);
        if (it != dataMgr->m_mapATLuoTag.end())
        {
            //  continue;
        }

        qt_sleep(200);

        const auto& [_, _v] = id;
        QStringList lstTag;
        int nDiffcult = 0;
        const QString strUrl =
            QString("https://www.luogu.com.cn/problem/AT_%1").arg(_v);
        addInfo(strUrl);
        if (!onePostLuogu(strUrl, lstTag, nDiffcult))
        {
            addInfo("Failed:" + contest->dt.toString("yyyy-MM-dd HH:mm:ss"));
            continue;
        }
        else
        {
            dataMgr->m_mapATLuoDifficulty[id] = nDiffcult;
            dataMgr->m_mapATLuoTag[id] = lstTag;
            dataMgr->ATSaveSpider();
            addInfo("OK: [" + lstTag.join(',') + "]-" +
                    contest->dt.toString("yyyy-MM-dd HH:mm:ss"));
        }
        // addInfo(QString("剩余%1个\r")
        //            .arg(setID.size() - dataMgr->m_mapATLuoTag.size()));
    }
}

bool SpiderDataDlg::onePostLuogu(const QString& strUrl, QStringList& lstTag,
                                 int& nDiffcult)
{
    lstTag.clear();
    bool bRet = false;
    auto m_dataMgr = DataMgr::getInstance();

    //
    QByteArray arByte = postLuogu.post(strUrl);

    QString strData = arByte;
    const char* szHeader = "\"tags\":[";
    int nFirst = strData.indexOf(szHeader);
    if (nFirst == -1)
    {
        QFile file("d:/1.txt");
        if (file.open(QIODevice::Truncate | QIODevice::WriteOnly))
        {
            file.write(arByte);
            file.close();
        }
        return false;
    }

    int nEnd = strData.indexOf("]", nFirst + strlen(szHeader));
    if (nEnd != -1 && nEnd > nFirst)
    {
        const QString strTmp =
            strData.mid(nFirst + strlen(szHeader),
                        nEnd - nFirst + 1 - strlen(szHeader) - 1);
        lstTag = strTmp.split(",");
        bRet = true;
    }

    //"difficulty":
    szHeader = "\"difficulty\":";
    nFirst = strData.indexOf(szHeader);
    if (nFirst == -1)
    {
        QFile file("d:/1.txt");
        if (file.open(QIODevice::Truncate | QIODevice::WriteOnly))
        {
            file.write(arByte);
            file.close();
        }
        return false;
    }

    nEnd = strData.indexOf(",", nFirst + strlen(szHeader));
    if (nEnd != -1 && nEnd > nFirst)
    {
        const QString strTmp =
            strData.mid(nFirst + strlen(szHeader),
                        nEnd - nFirst + 1 - strlen(szHeader) - 1);
        nDiffcult = strTmp.toInt();
        bRet = true;
    }

    return bRet;
}

void SpiderDataDlg::slotLuoguCF()
{
    m_log->clear();
    m_bStop = false;
    DataMgr* dataMgr = DataMgr::getInstance();
    const std::vector<CFProblemKey> setID = dataMgr->getCFProblemKey();
    for (const auto& id : setID)
    {
        if (m_bStop) break;

        CFContestRef contest = dataMgr->m_mapCFContest[id.nContestID];
        if (contest == nullptr)
        {
            addInfo("get contest is failed.");
            continue;
        }
        auto it = dataMgr->m_mapCFLuoTag.find(id);
        if (it != dataMgr->m_mapCFLuoTag.end())
        {
            //  continue;
        }
        qt_sleep(200);

        const auto& [_k, _v] = id;
        QStringList lstTag;
        int nDiffcult = 0;
        const QString strUrl =
            QString("https://www.luogu.com.cn/problem/CF%1%2").arg(_k).arg(_v);
        addInfo(strUrl);
        if (!onePostLuogu(strUrl, lstTag, nDiffcult))
        {
            addInfo("Failed-" + contest->dt.toString("yyyy-MM-dd HH:mm:ss"));
            continue;
        }
        else
        {
            dataMgr->m_mapCFLuoTag[id] = lstTag;
            dataMgr->CFSaveSpider();
            addInfo("OK: [" + lstTag.join(',') + "]-" +
                    contest->dt.toString("yyyy-MM-dd HH:mm:ss"));
        }
        // addInfo(QString("剩余%1个\r")
        //            .arg(setID.size() - dataMgr->m_mapCFLuoTag.size()));
    }
}